﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://wpfimageviewer.codeplex.com
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using BrainTechLLC;
using BrainTechLLC.WPF;
using System.Windows.Data;
using System.Diagnostics;
using System.Windows.Controls;
using System.Windows.Threading;
using System.Windows;
using System.Collections.Specialized;
using DirectoryViewModels;
using System.Threading;
using BrainTechLLC.ThreadSafeObjects;
using System.ComponentModel;

namespace BrainTechLLC.FolderExplorer
{
    public partial class FolderViewModel : BaseNotifyPropertyChangedUI
    {
        public FoldersViewModelCollection _subFolderList = new FoldersViewModelCollection();
        public bool _doneReadingSubfolders;
        public bool TotalFileCountDirty;
        public int OldTotalFileCount;
        public bool TotalSubdirCountDirty;
        protected string _FullPath;
        public int OldTotalSubdirCount;
        public DirectoryInfo _folder;
        private int _FileCount;
        public bool _Exclude;
        protected int _SubDirectoryCount;
        public bool _CorrectDirectorySize;
        public FoldersViewModelCollection _subFoldersForUIBinding;
        protected ObservableCollection<FileViewModel> _files;
        protected int _FolderExpansionLevel;
        protected bool _isSelected;
        protected bool _isEditMode;
        protected bool _isExpanded;
        protected string _currentFolder;
        protected DirectoriesToExamineViewModel _vm;
        protected FolderViewModel _parent;
        protected int _depth;
        public Grid _uiContainer;
        protected long _directorySize;
        public int _temp;
        protected bool _IsVisible;
        protected object _lockObject = new object();
        public bool _abortCalcDirSize;
        
        public string DirectoryDate
        {
            get { return _folder.CreationTime.ToShortDateString(); }
        }

        public int FileCount
        {
            get { return _FileCount; }
            set
            {
                if (_FileCount != value)
                {
                    _FileCount = value;
                    TotalFileCountDirty = true;
                    OnPropertyChanged("FileCount");

                    RecurseParents(f => { f.TotalFileCountDirty = true; }, false);
                }
            }
        }


        public int SubDirectoryCount
        {
            get { return _SubDirectoryCount; }
            set
            {
                if (_SubDirectoryCount != value)
                {
                    _SubDirectoryCount = value;
                    TotalSubdirCountDirty = true;
                    RaisePropertyChangedUIThread("SubDirectoryCount");

                    RecurseParents(f => { f.TotalSubdirCountDirty = true; }, false);
                }
            }
        }

        public int RecursiveSubDirectoryCount
        {
            get
            {
                if (!TotalSubdirCountDirty)
                {
                    return OldTotalSubdirCount;
                }

                TotalSubdirCountDirty = false;

                int count = SubDirectoryCount;

                if (SubFoldersCached != null)
                {
                    for (int n = 0; n < SubFoldersCached.Count; n++)
                    {
                        var folder = SubFoldersCached[n];
                        count += folder.RecursiveSubDirectoryCount;
                    }
                }

                OldTotalSubdirCount = count;
                return count;
            }
        }

        public int TotalFileCount
        {
            get
            {
                if (!TotalFileCountDirty)
                {
                    return OldTotalFileCount;
                }

                TotalFileCountDirty = false;

                int count = _FileCount;

                if (SubFoldersCached != null)
                {
                    for (int n = 0; n < SubFoldersCached.Count; n++)
                    {
                        count += SubFoldersCached[n].TotalFileCount;
                    }
                }

                OldTotalFileCount = count;
                return count;
            }
        }

        public bool IsParentVisible
        {
            get
            {
                if (_IsVisible) return true;
                return CrawlParentTreeAnyTrue(f => f.IsVisible);
            }
        }

        public bool IsVisible
        {
            get { return _IsVisible; }
            set
            {
                if (_IsVisible != value)
                {
                    _IsVisible = value;
                    if (_IsVisible == false)
                    {
                        if (_files != null && _files.Count > 0)
                        {
                            ClearFiles();
                        }
                        
                        if (_depth > 3 && _subFoldersForUIBinding != null && _subFoldersForUIBinding.Count > 0)
                        {
                            if (_subFoldersForUIBinding.FirstOrDefault(o => o.IsChecked || o.IsSelected) == null &&
                                _subFolderList.FirstOrDefault(o => o.IsChecked || o.IsSelected) == null)
                            {
                                bool okayToClear = true;

                                for (int n = 0; n < _subFolderList.Count; n++)
                                {
                                    var allSubFolders = _subFoldersForUIBinding[n].GetAllFoldersInMemory();
                                    if (allSubFolders.FirstOrDefault(o => o.IsVisible) != null)
                                    {
                                        okayToClear = false;
                                        break;
                                    }
                                }

                                if (okayToClear)
                                {
                                    ClearSubFolders();
                                }
                            }
                        }
                    }
                    OnPropertyChanged("IsVisible");
                }
            }
        }

        public FolderViewModel Parent
        {
            get { return _parent; }
        }

        public int Depth
        {
            get { return _depth; }
        }

        public string SelectedSpecialFolder
        {
            get
            {
                return _selectedSpecialFolder;
            }
            set
            {
                if (_selectedSpecialFolder != value)
                {
                    _selectedSpecialFolder = value;

                    if (!string.IsNullOrEmpty(_selectedSpecialFolder))
                    {
                        BreadthFirstSearch search = new BreadthFirstSearch();

                        var found = search.FindChild(this,
                            (folder) => folder.FullPath.Equals(_selectedSpecialFolder, StringComparison.OrdinalIgnoreCase),
                            (folder) => _selectedSpecialFolder.IndexOf(folder.FullPath, StringComparison.OrdinalIgnoreCase) == 0);

                        if (found != null)
                        {
                            found.IsExpanded = true;
                            found.IsSelected = true;
                        }
                    }

                    OnPropertyChanged("SelectedSpecialFolder");
                }
            }
        }

        public long DirectorySizeLong
        {
            get { return _directorySize; }
            set
            {
                if (_directorySize != value)
                {
                    _directorySize = value;
                    OnPropertyChanged("DirectorySizeLong");
                    OnPropertyChanged("DirectorySize");
                    OnPropertyChanged("DirectorySizeNonZero");
                }
            }
        }

        public string DirectorySize
        {
            get
            {
                if (_CorrectDirectorySize)
                {
                    return string.Format("{0:N0}KB", _directorySize / 1024);
                }
                else
                {
                    return string.Format("({0:N0}KB...)", _directorySize / 1024);
                }
            }
        }

        public string CurrentFolder
        {
            get
            {
                return _currentFolder;
            }
            set
            {
                if (_currentFolder != value)
                {
                    _currentFolder = value;
                    if (_currentFolder == _folder.FullName)
                    {
                        IsSelected = true;

                        if (!IsExpanded)
                        {
                            RecurseParents((folder) => folder.IsExpanded = true, true);
                            RecurseParents((folder) => folder.CurrentFolder = value, false);
                        }
                    }
                    OnPropertyChanged("CurrentFolder");
                }
            }
        }

        public bool Exclude
        {
            get { return _Exclude; }
            set
            {
                if (_Exclude != value)
                {
                    _Exclude = value;
                    if (_Exclude)
                    {
                        Complete = true;
                        _subFoldersForUIBinding = new FoldersViewModelCollection();
                        _files = new ObservableCollection<FileViewModel>();
                        _CorrectDirectorySize = true;
                        _directorySize = 0;
                        _FileCount = 0;
                        _abortCalcDirSize = true;
                    }
                    else
                    {
                        Complete = false;
                        _subFoldersForUIBinding = null;
                        _files = null;
                        _CorrectDirectorySize = false;
                        _directorySize = 0;
                        _FileCount = 0;
                        _abortCalcDirSize = false;
                    }
                    OnPropertyChanged("Exclude");
                }
            }
        }

        public List<string> SpecialFolders
        {
            get
            {
                List<string> results = new List<string>();

                results.Add(string.Empty);
                string s = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
                if (!string.IsNullOrEmpty(s)) results.Add(s);
                s = Environment.GetFolderPath(Environment.SpecialFolder.MyComputer);
                if (!string.IsNullOrEmpty(s)) results.Add(s);
                s = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                if (!string.IsNullOrEmpty(s)) results.Add(s);
                s = Environment.GetFolderPath(Environment.SpecialFolder.MyMusic);
                if (!string.IsNullOrEmpty(s)) results.Add(s);
                s = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
                if (!string.IsNullOrEmpty(s)) results.Add(s);

                for (int n = 0; n < _vm.Count; n++)
                {
                    results.Add(_vm[n].Directory);
                }

                return results;
            }
        }

        public DirectoriesToExamineViewModel DirectoriesToExamineViewModel
        {
            get { return _vm; }
            set
            {
                if (_vm != value)
                {
                    _vm = value;

                    if (_subFoldersForUIBinding != null)
                    {
                        for (int n = 0; n < _subFoldersForUIBinding.Count; n++)
                        {
                            _subFoldersForUIBinding[n].DirectoriesToExamineViewModel = value;
                        }
                    }
                    OnPropertyChanged("DirectoriesToExamineViewModel");
                }
            }
        }

        public bool IsChecked
        {
            get
            {
                var found = _vm == null ? null : _vm.FirstOrDefault((vm) => vm.Directory.Equals(FullPath, StringComparison.OrdinalIgnoreCase));
                return (found != null);
            }
            set
            {
                if (IsChecked != value)
                {
                    if (value == true)
                    {
                        _vm.TrackBeginAddDirectory();

                        Application.Current.Dispatcher.RunOnDispatcher(new VoidDelegate(() =>
                        {
                            DirectoriesView.AddDirCommand.Execute(_vm, FullPath);
                            OnPropertyChanged("IsChecked");
                            _vm._addedCount--;
                        }), DispatcherPriority.DataBind);   // was applicationIdle
                    }
                    else
                    {
                        var dirs = new List<string>();
                        dirs.Add(FullPath);
                        Application.Current.Dispatcher.RunOnDispatcher(new VoidDelegate(() =>
                        {
                            DirectoriesView.RemoveDirCommand.Execute(dirs, _vm);
                            OnPropertyChanged("IsChecked");
                        }), DispatcherPriority.DataBind);
                    }
                }
            }
        }

        public Visibility TextBoxEditVisible
        {
            get
            {
                return (_isEditMode) ? Visibility.Visible : Visibility.Collapsed;
            }
        }

        public Visibility NotTextBoxEditVisible
        {
            get
            {
                return (_isEditMode) ? Visibility.Collapsed : Visibility.Visible;
            }
        }

        public Thickness NotInEditModeThickness
        {
            get
            {
                if (_isEditMode)
                    return new Thickness(1, 1, 1, 1);
                else
                    return new Thickness(0);
            }
        }

        public bool NotInEditMode
        {
            get
            {
                return !_isEditMode;
            }
            set
            {
                if (NotInEditMode != value)
                {
                    IsEditMode = !value;
                    OnPropertyChanged("IsEditMode");
                    OnPropertyChanged("NotInEditMode");
                    OnPropertyChanged("NotInEditModeThickness");
                    OnPropertyChanged("TextBoxEditVisible");
                    OnPropertyChanged("NotTextBoxEditVisible");
                }
            }
        }

        public bool IsEditMode
        {
            get
            {
                return _isEditMode;
            }
            set
            {
                if (_isEditMode != value)
                {
                    _isEditMode = value;
                    OnPropertyChanged("IsEditMode");
                    OnPropertyChanged("NotInEditMode");
                    OnPropertyChanged("NotInEditModeThickness");
                    OnPropertyChanged("TextBoxEditVisible");
                    OnPropertyChanged("NotTextBoxEditVisible");
                }
            }
        }


        public bool IsExpanded
        {
            get
            {
                return _isExpanded;
            }
            set
            {
                if (_isExpanded != value)
                {
                    _isExpanded = value;

                    if (value == true && _parent != null)
                    {
                        _parent.IsExpanded = value;
                    }
                    if (value == false && _subFoldersForUIBinding != null)
                    {
                        _subFoldersForUIBinding.ForEach(f => f.IsExpanded = false);
                    }

                    OnPropertyChanged("IsExpanded");
                }
            }
        }

        public bool IsSelected
        {
            get
            {
                return _isSelected;
            }
            set
            {
                if (_isSelected != value)
                {
                    _isSelected = value;

                    if (_isSelected == true && _uiContainer != null)
                    {
                        Application.Current.Dispatcher.RunOnDispatcher(
                            new OneParamDelegate((o) =>
                            {
                                FolderViewModel vm = o as FolderViewModel;
                                if (vm.IsSelected)
                                {
                                    vm._uiContainer.BringIntoView();
                                }
                            }), DispatcherPriority.ApplicationIdle, this);
                    }

                    OnPropertyChanged("IsSelected");
                }
            }
        }

        public string Name
        {
            get
            {
                return this._folder.Name;
            }
            set
            {
                if (Name != value)
                {
                    _abortCalcDirSize = true;
                    Thread.Sleep(100);
                    Application.Current.Dispatcher.BeginInvoke(new VoidDelegate(() =>
                    {
                        string path = Path.GetDirectoryName(FullPath);
                        string newPath = path + value;
                        _folder.MoveTo(newPath);

                        this._folder = new DirectoryInfo(newPath);
                        _subFoldersForUIBinding = null;

                        OnPropertyChanged("FullPath");
                        OnPropertyChanged("Name");
                        OnPropertyChanged("SubFolders");
                    }), DispatcherPriority.Background);
                }
            }
        }

        public string FullPath
        {
            get
            {
                if (_FullPath == null)
                {
                    _FullPath = this._folder.FullName;
                }
                return _FullPath;
            }
            set
            {
                if (this._folder == null || this._folder.FullName != value)
                {
                    this._folder = new DirectoryInfo(value);
                    this._FullPath = null;
                    OnPropertyChanged("FullPath");
                }
            }
        }

        public ObservableCollection<FileViewModel> FilesCached
        {
            get { return this._files; }
        }

        public ObservableCollection<FileViewModel> Files
        {
            get
            {
                if (this._files == null)
                {
                    this._files = new ObservableCollection<FileViewModel>();

                    try
                    {
                        FileInfo[] fi = this._folder.GetFiles();

                        Application.Current.Dispatcher.BeginInvoke(new VoidDelegate(() => this.FileCount = fi.Length));

                        for (int i = 0; i < fi.Length; i++)
                        {
                            FileViewModel cf = new FileViewModel(fi[i]);
                            this._files.Add(cf);
                        }
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine(ex.ToString());
                    }
                }

                return this._files;
            }
        }

        public Visibility DirectorySizeNonZero
        {
            get { return _directorySize == 0 ? Visibility.Collapsed : Visibility.Visible; }
        }

        public FoldersViewModelCollection SubFoldersRequest
        {
            get
            {
                if (this._subFoldersForUIBinding == null)
                {
                    this._subFoldersForUIBinding = new FoldersViewModelCollection();
                    SubFoldersToRead.Enqueue(this);
                }
                return this._subFoldersForUIBinding;
            }
        }

        public FoldersViewModelCollection GetSubFoldersImmediate()
        {
            if (this._subFoldersForUIBinding == null)
            {
                lock (_lockObject)
                {
                    if (this._subFoldersForUIBinding == null)
                    {
                        this._subFoldersForUIBinding = new FoldersViewModelCollection();
                        this._subFolderList = new FoldersViewModelCollection();
                        ReadSubfolders(null);
                        return this._subFolderList;
                    }
                }
            }

            while (!_doneReadingSubfolders)
            {
                Thread.Sleep(10);
            }

            return this._subFolderList;
        }

        public FoldersViewModelCollection SubFolders
        {
            get
            {
                if (this._subFoldersForUIBinding == null)
                {
                    lock (_lockObject)
                    {
                        if (this._subFoldersForUIBinding == null)
                        {
                            this._subFoldersForUIBinding = new FoldersViewModelCollection();
                            CreateSubfolders();
                        }
                    }
                }
                return this._subFoldersForUIBinding;
            }
        }

        public void CreateSubfolders()
        {
            if (this._subFoldersForUIBinding == null || this._subFoldersForUIBinding.Count == 0)
            {
                _doneReadingSubfolders = false;
                ThreadPool.QueueUserWorkItem(new WaitCallback(ReadSubfolders));
            }
        }

        private void ReadSubfolders(object stateInfo)
        {
            ReadFoldersOnDemand(Int32.MaxValue, ref this._subFoldersForUIBinding, ref this._subFolderList);
            _doneReadingSubfolders = true;
        }

        public FoldersViewModelCollection SubFoldersCached
        {
            get { return _subFoldersForUIBinding; }
        }

        public int FolderExpansionLevel
        {
            get { return _FolderExpansionLevel; }
            set
            {
                if (_FolderExpansionLevel != value)
                {
                    if (!_FolderOperations.HasOperation("FolderExpansionLevel"))
                    {
                        _FolderExpansionLevel = value;
                        FindRoot().AddOperation("FolderExpansionLevel");

                        ThreadStart ts = new ThreadStart(DoFolderExpansion);
                        Thread t = new Thread(ts) { IsBackground = true, Priority = ThreadPriority.BelowNormal };
                        t.Name = "DoFolderExpansion";
                        t.Start();
                    }
                    OnPropertyChanged("FolderExpansionLevel");
                }
            }
        }

        public string CurrentWork
        {
            get
            {
                return _FolderOperations.OperationLookup.AllItems.BuildList(", ", o => o.Name);
            }
        }
    }
}
